/***
 * 富文本的undo redo实现
 * ***/
/***
 * 根据undo/redoc的记录创建一个内容被替换前的记录
 * ***/
Kedit.prototype.makeUndoRedoDataByRec = function (rec) {
    var nextRec;
    if(rec.isFloat){//浮动面板记录
        nextRec = {
            isFloat: true,    
            rebuild: !rec.rebuild,   
            id: rec.id, 
            html: rec.html
        };
        return nextRec;
    }
    nextRec = {
        isClear:rec.isClear,
        startRegionId: rec.startRegionId,
        endRegionId: rec.endRegionId,
        rangeOfs: rec.rangeOfs,
        replaceSectionId: [],
        html: [],
        inputId: rec.inputId,
        prevSectionId: rec.prevSectionId,
        nextSectionId: rec.nextSectionId
    };
    if (rec["isClear"]) {//如果是清空
        //this.$fixInput.trigger("mousedown");
        //this.moveToEnd(this.$fixInput.children().first());
        this._makeClearRec(nextRec);
    } else {//非清空操作
        var $input = this.$fixInput;
        if (rec.inputId) {
            $input = this.$inputWrap.children("div.k_edit_float_panel").find("#" + rec.inputId);
        }
        var p, id;
        for (var i = 0, len = rec.replaceSectionId.length; i < len; ++i) {
            id = rec.replaceSectionId[i];
            p = $input.find("#" + id);
            nextRec.replaceSectionId.push(id);//删除场景下，id对应的段落不一定存在
            if (p.length > 0) {
                
                nextRec.html.push(p[0].outerHTML);
            }
        }
    }
    return nextRec;
};

/**
 * 撤销操作
 * ***/
Kedit.prototype.undo = function (params, btn) {
    var rec = this.undoStack.pop();
    var _this = this;
    try {
        //console.log("execute undo >>>>>>>>>>>>>>>>>>>>>>>>>");
        //创建一个redo记录
        var nextRec = this.makeUndoRedoDataByRec(rec);
        var $input = this.$input;
        if (rec.inputId) {
            $input = this.$inputWrap.children("div.k_edit_float_panel").find("#" + rec.inputId);
            if ($input.length === 0) {
                return;
            }
        }
        this._recoverySectionContent(rec);
        this._putRedo(nextRec); //压入redo栈
    } catch (ex) {
        console.log("undo exception ");
    }
    if (this.undoStack.length === 0) {
        btn.addClass("k_edit_tools_disabled");
    }
};

/**
 *重做操作 
**/
Kedit.prototype.redo = function (params, btn) {
    var rec = this.redoStack.pop();
    //console.log("redo >>>>>>>>>>>>>> execute>>>>");
    try {
        //创建一个undo记录
        var nextRec = this.makeUndoRedoDataByRec(rec);
        var $input = this.$input;
        if (rec.inputId) {
            $input = this.$inputWrap.children("div.k_edit_float_panel").find("#" + rec.inputId);
            if ($input.length === 0) {
                return;
            }
        }
        this._recoverySectionContent(rec);
        this._putUndoNotClearRedo(nextRec); //压入undo栈
    } catch (ex) {
        console.log("redo exception");
    }
    if (this.redoStack.length === 0) {
        btn.addClass("k_edit_tools_disabled");
    }
};
/**
 * 撤销重做恢复 浮动面板里面的事件 
 * *****/
Kedit.prototype._recoveryFloatPanelEvents = function($fp){
    //console.log("recoveryFloatPanelEvents >>>> ");
    var inputWrap = this._bindFloatPanelEvents($fp);
    if (inputWrap) {
        var pres = inputWrap.children("div");
        this.buildObjectsEventsByTag(pres);
        var videoPreArray = [];
        pres.children("pre").each(function () {
            var $pre = $(this);
            if ($pre.hasClass("k_video_pre")) {
                videoPreArray.push($pre);
            }
        });
        var videoPre, videoOpt;
        for (var i = 0, len = videoPreArray.length; i < len; ++i) {
            videoPre = videoPreArray[i];
            videoOpt = JSON.parse(videoPre.attr("data").replace(/&quot;/g, "\""));
            this._createVideoPlayer(videoPre, videoOpt);
        }
        videoPreArray = undefined;
    }
}; 
/**
 *恢复undo\redo内容
* ***/
Kedit.prototype._recoverySectionContent = function (rec) {
   if(rec.isFloat) {
        if(rec.rebuild){//恢复面板
            var $fp = $(rec.html).appendTo(this.$inputWrap);
            this._recoveryFloatPanelEvents($fp);
        }else{//删除面板
            this.$inputWrap.children("#"+rec.id).remove();
            if (this.floatTools) {
                this.floatTools.hide();
            }
            if (this.floatPropPanel) {
                this.floatPropPanel.hide();
            }
        } 
        return;
    }
    var repalceIds = rec["replaceSectionId"];
    var firstRecoverSection;
    var recovertyHtml = rec["html"];
    var firstAndLastNewSection = [];
    var _this = this;
    var i, len, id, rmSection, $p, $input = this.$input;
    if (rec.inputId) {
        $input = this.$inputWrap.children("div.k_edit_float_panel").find("#" + rec.inputId);
    }
    //先移除需要替换的段落，移除时候插入第一个段落，其他待恢复的段落均插入这个新的第一个段落
    for (i = 0, len = repalceIds.length; i < len; ++i) {
        id = repalceIds[i];
        rmSection = $input.children("#" + id);
        if (!firstRecoverSection && rmSection.length > 0 && recovertyHtml.length > 0) {
            firstRecoverSection = $(recovertyHtml[0]).insertAfter(rmSection);
        }
        if (rmSection.length > 0) {
            this._removeSection(rmSection);
        }
    }
    if (firstRecoverSection) {//已经恢复了第一个段落，继续恢复其他段落
        this.buildObjectsEventsByTag(firstRecoverSection);
        firstAndLastNewSection.push(firstRecoverSection);
        if (recovertyHtml.length === 1) {
            firstAndLastNewSection.push(firstRecoverSection);
        }
        if(rec.isClear){//清空场景
            var $allPanel = this.$inputWrap.children(".k_edit_float_panel").each(function(){
                var $w = $(this);
                $w.children(".k_editor_float_input").children().each(function(){
                    _this._removeSection($(this));
                });
            });
            $allPanel.remove();
            firstRecoverSection.nextAll().each(function(){
                _this._removeSection($(this));
            });
        }
        //恢复其他段落内容
        var targetSection = firstRecoverSection;
        for (i = 1, len = recovertyHtml.length; i < len; ++i) {
            $p = $(recovertyHtml[i]);
            $p.insertAfter(targetSection);
            this.buildObjectsEventsByTag($p);
            targetSection = $p;
            if (i === len - 1) {
                firstAndLastNewSection.push(targetSection);
            }
        }      
    } else {//全删除场景，当前input中找不到rec["replaceSectionId"]对应的dom，则依赖 prevSectionId,nextSectionId进行恢复
        if (rec["prevSectionId"]) {
            var prevPel = $input.children("#" + rec["prevSectionId"]);
            for (i = 0, len = recovertyHtml.length; i < len; ++i) {
                $p = $(recovertyHtml[i]);
                $p.insertAfter(prevPel);
                this.buildObjectsEventsByTag($p);
                prevPel = $p;
                if (i === 0) {
                    firstAndLastNewSection.push(prevPel);
                }
                if (i === len - 1) {
                    firstAndLastNewSection.push(prevPel);
                }
            }
        } else if (rec["nextSectionId"]) {
            var nextPel = $input.children("#" + rec["nextSectionId"]);
            for (i = 0, len = recovertyHtml.length; i < len; ++i) {
                $p = $(recovertyHtml[i]);
                $p.insertBefore(nextPel);
                this.buildObjectsEventsByTag($p);
                if (i === 0) {
                    firstAndLastNewSection.push($p);
                }
                if (i === len - 1) {
                    firstAndLastNewSection.push($p);
                }
            }
        }
    }
    if (rec["floatItems"]) {
        var $t;
        len = rec["floatItems"].length;
        if (len > 0) {
            for (i = 0; i < len; ++i) {
                $t = $(rec["floatItems"][i]).appendTo(this.$inputWrap);
                this._recoveryFloatPanelEvents($t);
            }
        } else {
            this.$inputWrap.children(".k_edit_float_panel").remove();
        }
    }
    try{
        this._rebulidUndoRedoRange(rec, firstAndLastNewSection);
    }catch(e1){
        console.log("_rebulidUndoRedoRange exception  ");
    }   
    return firstAndLastNewSection;
};

/**undo redo后重建选区**/
Kedit.prototype._rebulidUndoRedoRange = function (rec, newSections) {
    //console.log("_rebulidUndoRedoRange >>>>>>>> _rebulidUndoRedoRange");    
    if (!rec["startRegionId"] || !rec.rangeOfs || $.isEmptyObject(rec.rangeOfs)) {
        //不需要恢复选区
        return;
    }
    var rangeOfs = rec.rangeOfs;
    var startId = rec["startRegionId"];
    var endId = rec["endRegionId"];
    var startSection,
        endSection,
        firstChild,
        endChild;
    var $input = this.$input;
    if (rec.inputId) {
        $input = this.$inputWrap.children("div.k_edit_float_panel").find("#" + rec.inputId);
    }
    if (rec["isClear"]) {//清空操作,恢复第一行为光标行
        if(!$.isEmptyObject(rangeOfs) && startId && endId){
            var allChild = $input.children();
            firstChild = allChild.find("#" + startId);
            endChild = allChild.find("#" + endId);
            if(firstChild.length > 0 && endChild.length > 0){
                firstChild = firstChild[0].firstChild;
                endChild = endChild[0].lastChild;
                this.setCrossRange(firstChild, endChild, rangeOfs.startOffset, rangeOfs.endOffset);
            }else{
                this.moveToEnd($input.children().first());
            }
        }else{
            this.moveToEnd($input.children().first());
        }       
    } else {
        //取 prevSectionId,nextSectionId 进行恢复选区
        if (newSections.length === 0) {
            if (rec.prevSectionId) {
                startSection = $input.children("#" + rec.prevSectionId);
                if (startSection.length === 0) {
                    startSection = undefined;
                }
            }
            if (rec.nextSectionId) {
                endSection = $input.children("#" + rec.nextSectionId);
                if (endSection.length === 0) {
                    endSection = undefined;
                }
            }
        } else {
            startSection = newSections[0];
            if (newSections.length > 1) {
                endSection = newSections[1];
            } else {
                endSection = startSection;
            }
        }
        if (!startSection || !endSection) {
            console.log("恢复选区无效！");
            return;
        }
        //先从开始段落查询选区的开始/结束节点
        firstChild = startSection.find("#" + startId);
        endChild = endSection.find("#" + endId);

        //如果没有找到则从结束段落里面取
        if (firstChild.length === 0) {
            firstChild = endSection.find("#" + startId);
        }
        if (endChild.length === 0) {
            endChild = endSection.find("#" + endId);
        }
        this._clearRangeRecord();
        var parent;
        if (firstChild.length > 0 && endChild.length > 0) {
            parent = firstChild;
            firstChild = firstChild[0].firstChild;
            endChild = endChild[0].lastChild;
            this.setCrossRange(firstChild, endChild, rangeOfs.startOffset, rangeOfs.endOffset);
        }
        //补充回激活对象
        //startSection endSection中是否存在table对象            
        if (parent && startSection[0].firstChild.nodeName === "TABLE" || endSection[0].firstChild.nodeName === "TABLE") {
            var objectTag;
            while (parent.length > 0 && parent[0].tagName !== "DIV") {
                if (parent[0].tagName === "TABLE") {
                    objectTag = parent;
                }
                parent = parent.parent();
            }
            if (objectTag) { //找到了对象
                this.clickActivedObject = objectTag.data("object");
            }
        }
    }
};

/**
 *重置undo、redo 
**/
Kedit.prototype.resetUndoRedo = function () {
    this.undoStack.clear();
    this.redoStack.clear();
};

/**
*压入一个撤销, 不清理redo ，撤销记录存放在this["undoRedoRec"]
* notClearRedo 不清理redo 
* nextRec:记录,如果传参 ，则优先nextRec 
**/
Kedit.prototype._putUndoNotClearRedo = function (nextRec) {
    var rec = this["undoRedoRec"];
    this["undoRedoRec"] = undefined;
    if (nextRec) {
        rec = nextRec;
    }
    if (rec) {
        this.undoStack.push(rec);
        this._undoRedoNotify("undo");
        return true;
    }
    return false;
};

/***
 * 按鼠标焦点所在段落提取undo记录，并putUndo
 * ***/
Kedit.prototype.putUndoByFocus = function(){
    this._makeUndoDataByFocus();
    this._putUndo();
};
/**
*压入一个撤销，撤销记录存放在this["undoRedoRec"]
* 并清理redo 
* nextRec:记录,如果传参 ，则优先nextRec 
**/
Kedit.prototype._putUndo = function (nextRec) {
    if (this._putUndoNotClearRedo(nextRec)) {
        this.redoStack.clear(); //一旦有undo进来，redo必须清理
        this._undoRedoNotify("redo");
    }
};
/***
 * 压入一个redo this["undoRedoRec"]
 * nextRec:记录,如果传参 ，则优先nextRec 
 * ***/
Kedit.prototype._putRedo = function (nextRec) {
    var rec = this["undoRedoRec"];
    this["undoRedoRec"] = undefined;
    if (nextRec) {
        rec = nextRec;
    }
    if (rec) {
        this.redoStack.push(rec);
        this._undoRedoNotify("redo");
    }
};
/***连续操作下，利用timeout delay毫秒（默认500）
 *  保证只压入一次undo
 *  delay = 0 表示不延时  
 * ***/
Kedit.prototype._putUndoDelayed = function (delay) {
    var timer = 600;
    if (delay === 0) {
        this._putUndo();
    } else {
        if (typeof delay !== "undefined") {
            timer = delay;
        }
        clearTimeout(this["_putUndoTimer"]);
        var _this = this;
        this["_putUndoTimer"] = setTimeout(function () {
            _this._putUndo();
        }, timer);
    }
};
/**清理this["undoRedoRec"]**/
Kedit.prototype._clearUndoRedoRecVar = function () {
    this["undoRedoRec"] = undefined;
};
/**
 * 创建撤销重做里当前的选区记录
 * ***/
Kedit.prototype._makeUndoRedoRangeRec = function(rec){
    if (this.range) {//如果有选区,则需要恢复选区
        var startContainer = this.range.startContainer; //开始节点
        var endContainer = this.range.endContainer; //结束节点 
        var startNode = $(startContainer.parentNode);
        var endNode = $(endContainer.parentNode);
        var startId = startNode.attr("id");
        var endId = endNode.attr("id");
        rec["startRegionId"] = startId;
        rec["endRegionId"] = endId;
        rec["rangeOfs"] = {
            startOffset: this.range.startOffset,
            endOffset: this.range.endOffset
        };
    }
};
/***
 * 创建undo/redo记录by 开始/结束段落
 * ***/
Kedit.prototype._makeUndoRedoDataByPelement = function (startPelment, endEelement) {
    /**已经存在的没有压栈的记录**/
    if (this["undoRedoRec"]) {
        return this["undoRedoRec"];
    }
    var inputId = this.$input.attr("id");
    var rec = {
        startRegionId: undefined,
        endRegionId: undefined,
        rangeOfs: undefined,
        replaceSectionId: [],
        html: [],
        inputId: inputId,
        prevSectionId: undefined,
        nextSectionId: undefined
    };
    var prevPel = startPelment.prev();
    var nextPel = endEelement.next();
    if (prevPel.length > 0) {
        rec.prevSectionId = prevPel.attr("id");
    }
    if (nextPel.length > 0) {
        rec.nextSectionId = nextPel.attr("id");
    }
    var tmpEl = startPelment;
    rec.replaceSectionId.push(tmpEl.attr("id"));
    rec.html.push(tmpEl[0].outerHTML);
    while (tmpEl.length > 0 && tmpEl[0] !== endEelement[0]) {
        tmpEl = tmpEl.next();
        rec.replaceSectionId.push(tmpEl.attr("id"));
        rec.html.push(tmpEl[0].outerHTML);
    }
    this._makeUndoRedoRangeRec(rec);
    return rec;
};
/***
 * 创建当前el节点所在段落的undo记录
 * ***/
Kedit.prototype._putUndoDataByNodeEl = function (el){   
    var pelement = this._findNodePElement(el);
    var rec = this._makeUndoRedoDataByPelement(pelement,pelement);
    this._putUndo(rec);
};

/**
 * 根据鼠标所在的焦点，以段落为重做、撤销恢复单位
 * startContainer、endContainer必定为文本节点
 * 记录选区位置内所有段落的html到待恢复的html数组中
 * isClear:是否是清空操作
 * rec={
 * startRegionId:选区的开始节点id,
 * endRegionId:选区的结束节点id ,
 * rangeOfs = { startOffset : 选区开始节点的下标索引 , endOffset: 选区结束节点的下标索引}
 * replaceSectionId = [被操作段落id集合] ,
 * html:[被操作的段落html集合],
 * inputId: 如果是浮动文本，则为浮动文本输入框的id，否则为空
 * isClear:是否是清除操作,
 * prevSectionId: 被操作段落选区的前一个段落（如果存在）, 应对选区所有段落被完整删除场景
 * nextSectionId: 被操作段落选区的下一个段落（如果存在）, 应对选区所有段落被完整删除场景
 * }
 * ***/
Kedit.prototype._makeUndoDataByFocus = function (isClear) {
    //console.log("_makeUndoDataByFocus  >>>>>>");
    /**已经存在的没有压栈的记录**/
    if (this["undoRedoRec"]) {
        return this["undoRedoRec"];
    }
    var prevPEle, nextPEle;
    var inputId = this.$input.attr("id");
    var rec = {
        isClear:isClear,
        startRegionId: undefined,
        endRegionId: undefined,
        rangeOfs: undefined,
        replaceSectionId: [],
        html: [],
        inputId: inputId,
        prevSectionId: undefined,
        nextSectionId: undefined
    };
    var _this = this;
    this._makeUndoRedoRangeRec(rec);
    if (isClear) {//如果是清除
        this._makeClearRec(rec);
    } else {
        if (this.clickActivedObject && this.clickActivedObject.jqObj) { //修饰的是表格、图片等对象
            var ele = this.clickActivedObject.jqObj.parent();
            while (ele.length > 0) {
                if (ele.hasClass("_section_div_")) {
                    break;
                }
                ele = ele.parent();
            }
            rec["replaceSectionId"].push(ele.attr("id"));
            rec["html"].push(ele[0].outerHTML);
            prevPEle = ele.prev();
        } else {
            this._getRangeSections(function (p) {
                if (!prevPEle) {
                    prevPEle = p.prev();
                }
                rec["html"].push(p[0].outerHTML);
                rec["replaceSectionId"].push(p.attr("id"));
                nextPEle = p.next();
            });
        }
    }
    //补充上 选区范围内的 前后段落 ID 记录（如果存在！）
    if (prevPEle && prevPEle.length > 0) {
        rec.prevSectionId = prevPEle.attr("id");
    }
    if (nextPEle && nextPEle.length > 0) {
        rec.nextSectionId = nextPEle.attr("id");
    }
    this["undoRedoRec"] = rec;    
    return rec;
};
/**
 * 创建一个清空记录
 * ***/
Kedit.prototype._makeClearRec = function (rec) {
    var childs = this.$fixInput.children("div");
    var _this = this;
    childs.each(function () {
        var $p = $(this);  
        rec.html.push($p[0].outerHTML);
        rec.replaceSectionId.push($p.attr("id"));
    });
    rec["floatItems"] = [];//补充上浮动文本/图片
    this.$inputWrap.children(".k_edit_float_panel").each(function () {
        var $tag = $(this).clone();
        $tag.children(".k_editor_float_input").removeAttr("contenteditable");
        var phtml = $tag[0].outerHTML.replace(/cursor:\s*((?!;).)+;/g, "");
        rec["floatItems"].push(phtml);
    });
    // if (!this.range){//恢复第一个段落为光标段落           
    // }
};
/***
 * 删除面板时候，将浮动面板压入撤销记录
 * jqObj : 面板jq对象
 * ***/
Kedit.prototype.putFlowPanel2Undo = function (jqObj) {
    //console.log("putFlowPanel2Undo >>>>>");
    var _this = this;
    var rec = {
        isFloat: true,    
        rebuild: true,   
        id: jqObj.attr("id"), 
        html: jqObj[0].outerHTML
    };
    this._putUndo(rec);
};